#include "renderer.h"

#define BUFFER_OFFSET(i) ((char *)NULL + (i))
#define PI 3.1415926535897

Renderer::Renderer()
{
}

void Renderer::set_lighting(){


}


void Renderer::render_scene(Scene *scene){
    QList<Object*>::iterator objects_iter;
    QList<Octree*>::iterator octrees_iter;
    draw_grid();
    if(scene != NULL){

        for(objects_iter =scene->m_objects.begin(); objects_iter != scene->m_objects.end();  objects_iter++){
            if((*objects_iter)->is_visible()){
                render_object(*objects_iter);
            }
        }
        for(octrees_iter = scene->m_octrees.begin(); octrees_iter != scene->m_octrees.end();  octrees_iter++){
            if((*octrees_iter)->is_visible()){
                render_octree(*octrees_iter);
            }
        }

    }
}

void Renderer::render_octree(Octree *octree){
    glColor3f(1.0,0.0,0.0);
    QList<OctreeNode*>::iterator node_iter;
    bool show_in = octree->get_nodes_visibility(Inside);
    bool show_out = octree->get_nodes_visibility(Outside);
    bool show_part = octree->get_nodes_visibility(Partially);
    bool wireframe = octree->wireframe();
    bool solid  = octree->solid();
    glPushMatrix();
    glMultMatrixf(&(octree->m_root_frame->local_to_world())[0][0]);
    for(node_iter = octree->m_nodes.begin(); node_iter != octree->m_nodes.end(); node_iter++){
        if((**node_iter).m_is_leaf){
            draw_node(**node_iter,solid,wireframe,show_in,show_out,show_part);
        }
    }
    glPopMatrix();
}

void Renderer::render_object(Object *obj){    
    switch(obj->m_primitive->type){
    case BoxPrimitive:
        Box *cube;
        cube = dynamic_cast<Box*>(obj->m_primitive);
        glPushMatrix();
        glMultMatrixf(&(obj->m_primitive->m_frame->local_to_world())[0][0]);
        glColor3f(1.0,1.0,1.0);
        if(obj->solid()){
            glEnable(GL_POLYGON_OFFSET_FILL);
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
            glPolygonOffset(1.0f,1.0f);
            draw_cube(*cube);
            glDisable(GL_POLYGON_OFFSET_FILL);
        }
        if(obj->wireframe()){
            glColor3f(0.0,0.0,0.0);
            glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);            
            draw_cube(*cube);           
        }
        glPopMatrix();
        break;
    case SpherePrimitive:
        Sphere *sphere;
        sphere = dynamic_cast<Sphere*>(obj->m_primitive);
        glPushMatrix();
        glMultMatrixf(&(obj->m_primitive->m_frame->local_to_world())[0][0]);
        glColor3f(1.0,1.0,1.0);
        if(obj->solid()){
            glEnable(GL_POLYGON_OFFSET_FILL);
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
            glPolygonOffset(1.0f,1.0f);
            draw_sphere(sphere);
            glDisable(GL_POLYGON_OFFSET_FILL);
        }
        if(obj->wireframe()){
            glColor3f(0.0,0.0,0.0);
            glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
            draw_sphere(sphere);
        }
        glPopMatrix();
        break;
    }
}

void Renderer::draw_cube(Box &c){   

//    glPushMatrix();

//        static int temp = 0;

//        if (temp == 0) {
//            temp++;
//            c.translate(1, 1, 1);
//            c.rotateX(45);
//        }

//        glMultMatrixf(&(c.getToWorldMatrix()[0][0]));
//        glMultMatrixf(&(c.getToLocalMatrix()[0][0]));


    glBegin(GL_QUADS);
    //Front face
    glVertex3f(c.m_points[4].x, c.m_points[4].y, c.m_points[4].z);
    glVertex3f(c.m_points[5].x, c.m_points[5].y, c.m_points[5].z);
    glVertex3f(c.m_points[6].x, c.m_points[6].y, c.m_points[6].z);
    glVertex3f(c.m_points[7].x, c.m_points[7].y, c.m_points[7].z);

    //Back face
    glVertex3f(c.m_points[0].x, c.m_points[0].y, c.m_points[0].z);
    glVertex3f(c.m_points[1].x, c.m_points[1].y, c.m_points[1].z);
    glVertex3f(c.m_points[2].x, c.m_points[2].y, c.m_points[2].z);
    glVertex3f(c.m_points[3].x, c.m_points[3].y, c.m_points[3].z);

    //Top face
    glVertex3f(c.m_points[3].x, c.m_points[3].y, c.m_points[3].z);
    glVertex3f(c.m_points[7].x, c.m_points[7].y, c.m_points[7].z);
    glVertex3f(c.m_points[6].x, c.m_points[6].y, c.m_points[6].z);
    glVertex3f(c.m_points[2].x, c.m_points[2].y, c.m_points[2].z);

    //Bottom face
    glVertex3f(c.m_points[0].x, c.m_points[0].y, c.m_points[0].z);
    glVertex3f(c.m_points[4].x, c.m_points[4].y, c.m_points[4].z);
    glVertex3f(c.m_points[5].x, c.m_points[5].y, c.m_points[5].z);
    glVertex3f(c.m_points[1].x, c.m_points[1].y, c.m_points[1].z);

    //Left face
    glVertex3f(c.m_points[0].x, c.m_points[0].y, c.m_points[0].z);
    glVertex3f(c.m_points[4].x, c.m_points[4].y, c.m_points[4].z);
    glVertex3f(c.m_points[7].x, c.m_points[7].y, c.m_points[7].z);
    glVertex3f(c.m_points[3].x, c.m_points[3].y, c.m_points[3].z);

    //Right face
    glVertex3f(c.m_points[1].x, c.m_points[1].y, c.m_points[1].z);
    glVertex3f(c.m_points[5].x, c.m_points[5].y, c.m_points[5].z);
    glVertex3f(c.m_points[6].x, c.m_points[6].y, c.m_points[6].z);
    glVertex3f(c.m_points[2].x, c.m_points[2].y, c.m_points[2].z);

    glEnd();

//    glBindBuffer(GL_ARRAY_BUFFER,c.m_vbo_id);
//    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, c.m_index_vbo);
//    glEnableClientState(GL_VERTEX_ARRAY);
//    glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));
//    glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
//    glDisableClientState(GL_VERTEX_ARRAY);
//    glBindBufferARB(GL_ARRAY_BUFFER, 0);
//    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


}

void Renderer::draw_sphere(Sphere *sphere){
    float radius = sphere->m_radius;
    float x, y, z;
    float c = PI/180;    
    for(int phi = -90; phi <= 90; phi+=10){
        glBegin(GL_QUAD_STRIP);

        for(int theta = -180; theta <= 180; theta+=10){
            x = radius*sin(c * theta)*cos(c * phi);
            y = radius*cos(c * theta)*cos(c * phi);
            z = radius*sin(c * phi);

            glVertex3f(x,y,z);

            x = radius*sin(c * theta)*cos(c * (phi + 20));
            y = radius*cos(c * theta)*cos(c * (phi + 20));
            z = radius*sin(c * (phi + 20));

            glVertex3f(x,y,z);
        }

        glEnd();
    }
//    glBindBuffer(GL_ARRAY_BUFFER,sphere->m_vbo_id);
//    glEnableClientState(GL_VERTEX_ARRAY);
//    glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));
//    glDrawArrays(GL_QUAD_STRIP, 0, sphere->num_vertices/3);
//    glDisableClientState(GL_VERTEX_ARRAY);
//    glBindBufferARB(GL_ARRAY_BUFFER, 0);
}

void Renderer::draw_node(OctreeNode &node, bool solid, bool wireframe, bool show_in, bool show_out, bool show_part){

     if(solid){
        glEnable(GL_POLYGON_OFFSET_FILL);
        glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
        glPolygonOffset(1.0f,1.0f);
        if ( node.m_state == Inside && show_in) {
            glColor3f(0.3f, 0.5f, 0.9f);
            draw_cube(node.m_node_boundbox);           
        }
        if ( node.m_state == Outside && show_out) {
            glColor3f(1.0f, 1.0f, 1.0f);
            draw_cube(node.m_node_boundbox);           
        }
        if ( node.m_state == Partially && show_part) {
            glColor3f(0.7f, 0.0f, 0.3f);
            draw_cube(node.m_node_boundbox);            

        }
        glDisable(GL_POLYGON_OFFSET_FILL);
    }
    if(wireframe){
        glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);       
        if ( node.m_state == Inside && show_in) {
            glColor3f(0.0f,0.0f,0.0f);
            draw_cube(node.m_node_boundbox);
        }
        if ( node.m_state == Outside && show_out) {
            glColor3f(0.0f,0.0f,0.0f);
            draw_cube(node.m_node_boundbox);
        }
        if ( node.m_state == Partially && show_part) {
            glColor3f(0.0f,0.0f,0.0f);
            draw_cube(node.m_node_boundbox);
        }
    }
}


void Renderer::draw_grid(){
    //int size = 0.5;
    glColor3f(0.9,0.9,0.9);
    glBegin(GL_LINES);
    for(int i = -1000; i < 1000; i++){
        glVertex3f(-1000,0,i);
        glVertex3f(1000,0,i);
    }
    for(int i = -1000; i < 1000; i++){
        glVertex3f(i,0,-1000);
        glVertex3f(i,0,1000);
    }
    glEnd();
}



